<?php
/**
* This static class handles all communications with the current Scribble
* session.
*
* Copyright (c) 2009 James Gauld <james@jamesgauld.net>
* This file is part of Scribble.
* @license http://www.scribblecms.co.uk/license.txt
*
* @package Scribble
*/
use Buan\Config;
use Buan\Model;
define('SCRIBBLE_SESSION_VAR', "ScribbleSession_".md5(Config::get('app.docRoot')));
abstract class ScribbleSession {

	/**
	* Holds all users occupying the current session. The first in this queue is
	* considered the "active" user whilst all other lie dormant until they are
	* requested.
	*
	* @var array
	*/
	private static $userQueue = array();

	/**
	* Returns the active user (ie. the user at the front of the queue).
	*
	* @return UserModel|NULL
	*/
	public static function getUser() {
		return !empty(self::$userQueue) ? self::$userQueue[0] : NULL;
	}

	/**
	* Returns the whole user queue.
	*
	* @return array
	*/
	public static function getUserQueue() {
		return self::$userQueue;
	}

	/**
	* Initialises the session. This must be called sometime after a call to
	* "session_start()"
	*
	* @return void
	* @throws Exception If session couldn't be setup for any reason.
	*/
	public static function init() {

		// Check that a session has been started, otherwise we can't continue so
		// throw an Exception
		if(session_id()=='') {
			throw new Exception("Cannot initialise a Scribble session until a PHP session has been started");
		}

		// Setup the session variables
		if(!isset($_SESSION[SCRIBBLE_SESSION_VAR])) {
			$_SESSION[SCRIBBLE_SESSION_VAR] = array(
				'userQueue'=>array()
			);
		}

		// If there are any users in the queue, copy them into the local queue
		foreach($_SESSION[SCRIBBLE_SESSION_VAR]['userQueue'] as $qu) {
			$user = Model::create('User');
			$user->populateFromArray($qu);
			$user->isInDatabase(TRUE);
			$user->hasChanged(FALSE);
			self::$userQueue[] = $user;
		}
	}

	/**
	* Tests is anyone is logged into the current session.
	*
	* @return bool
	*/
	public static function isOccupied() {
		return !empty(self::$userQueue);
	}

	/**
	* Adds the specified user to the front of the internal queue.
	*
	* @param UserModel User instance to add. Must be persistent.
	* @return bool If the user cannot be added to the queue then FALSE is returned
	*/
	public static function login(UserModel $user) {

		// User model must be persistent, so fail if it's not
		if(!$user->isInDatabase()) {
			return FALSE;
		}

		// If the user is already in the queue, remove all succeeding users in order
		// to bring $user to the front
		foreach(self::$userQueue as $k=>$qu) {
			if($qu->id===$user->id) {
				reset(self::$userQueue);
				self::$userQueue = array_slice(self::$userQueue, $k);
				self::updateQueue();
				return TRUE;
			}
		}

		// Add to queue
		array_unshift(self::$userQueue, $user);
		self::updateQueue();
		return TRUE;
	}

	/**
	* Removes the specified user, and all succeeding users, from the queue.
	* If no user is specified then all users are removed, effectively logging the
	* browser out of Scribble.
	*
	* @param UserModel|NULL User to remove
	* @return void
	*/
	public static function logout($user=NULL) {
		if($user===NULL) {
			self::$userQueue = array();
		}
		else {
			foreach(self::$userQueue as $k=>$qu) {
				if($qu->id===$user->id) {
					reset(self::$userQueue);
					self::$userQueue = $k==count(self::$userQueue)-1 ? array() : array_slice(self::$userQueue, $k);
				}
			}
		}
		self::updateQueue();
	}

	/**
	* Updates the queue held in the session variable by copying data from
	* self::$userQueue to the session.
	*
	* This method must be called everytime there is a change to self::$userQueue
	*
	* @return void
	*/
	private static function updateQueue() {
		$_SESSION[SCRIBBLE_SESSION_VAR]['userQueue'] = array();
		foreach(self::$userQueue as $qu) {
			$_SESSION[SCRIBBLE_SESSION_VAR]['userQueue'][] = $qu->getDbData();
		}
	}

	/**
	* If you make any persistent changes to a User that may be in the session
	* queue, then you should call this method to ensure the data in the session
	* matches that in the database.
	*
	* @param UserModel The User from which data will be taken
	* @return void
	*/
	public static function updateUser(UserModel $user) {

		// Find the user in the queue and update
		foreach(self::$userQueue as $k=>$qu) {
			if($qu->id===$user->id) {
				reset(self::$userQueue);
				$qu->populateFromArray($user->getDbData());
				$qu->hasChanged(FALSE);
				break;
			}
		}
		self::updateQueue();
	}

	/**
	* Checks if a User with the specified ID is logged into the session.
	*
	* @param string User ID
	& @return bool
	*/
	public static function isUserQueued($userId) {
		foreach(self::$userQueue as $qu) {
			if($qu->id===$userId) {
				reset(self::$userQueue);
				return TRUE;
			}
		}
		return FALSE;
	}
}
?>